
<template>
  <div :class="['AIConsulting_container', styleClass]">

    <div
      :class="['AIConsulting_message_container', styleClass]"
      ref="messageListContainer"
    >
      <template v-for="(item, index) in messageList">
        <!-- <div
          v-if="!item.ready"
          :key="index"
          :class="
            item.source === 'user'
              ? 'AIConsulting_message_item'
              : 'AIConsulting_message_item_ai'
          "
        >
          {{ item.answer }}
        </div>
        <div
          :class="
            item.source === 'user'
              ? 'AIConsulting_message_item'
              : 'AIConsulting_message_item_ai'
          "
          v-else
          v-html="item.markDownText || ''"
        ></div> -->
        <div
          :class="
            item.source === 'user'
              ? 'AIConsulting_message_item'
              : 'AIConsulting_message_item_ai'
          "
          v-html="renderMarkDown(item.answer || '')"
        ></div>
        
      </template>
      <div
        :class="[
          'AIConsulting_message_loading',
          styleClass,
          messageLoadingStatus ? '' : 'AIConsulting_message_loading_hidden',
        ]"
      >
        <div
          :class="[
            'AIConsulting_message_loading_dot',
            'AIConsulting_message_loading_dot_1',
          ]"
        ></div>
        <div
          :class="[
            'AIConsulting_message_loading_dot',
            'AIConsulting_message_loading_dot_2',
          ]"
        ></div>
        <div
          :class="[
            'AIConsulting_message_loading_dot',
            'AIConsulting_message_loading_dot_3',
          ]"
        ></div>
      </div>
    </div>
    <div :class="['AIConsulting_agent_container', styleClass]" >
      <div class="AIConsulting_agent_sendMessageToAgentContainer">
        <div class="AIConsulting_agent_sendMessageToAgent">
          <a-textarea
            v-model:value="inputVal"
            :disabled="['agent_thought', 'message', 'agent_message'].includes(currentEvent) || messageLoadingStatus"
            :bordered="false"
            :placeholder="inputTypeList[currentInputType].placeholder"
            class="AIConsulting_agent_sendMessageToAgent_input"
            @pressEnter="handlePressEnter"
            :auto-size="{ minRows: 1, maxRows: 2 }"
            v-if="currentInputType === 0"
          />
          <div
            class="AIConsulting_agent_sendMessageToAgent_microphone"
            v-else
            @mousedown="startRecognition"
            @mouseup="stopRecognition"
          >
            {{ inputTypeList[currentInputType].placeholder }}
          </div>
          <!-- <div class="stop" v-if="['message', 'agent_message'].includes(currentEvent)" @click="handleStop">
            <BorderOutlined />
          </div>  -->
        </div>
        <div
          class="AIConsulting_agent_sendMessageToAgent_mute"
          @click="switchVolumeType"
        >
        </div>
      </div>
    </div>
    <div class="agentImg" :style="{ backgroundImage: `url(${agentImg})` }"></div>
  </div>
  <context-holder />
  <!-- <audio autoplay controls>
        <source :src="TTS_str_BASE64" type="audio/mp3" />
    </audio> -->
</template>

<!-- 
    1. 该组件是AI咨询组件，分为两个大小：small和middle，由props size决定，默认为middle
    2. 该组件还可以选择与哪个agent沟通，详情见publicConfig.js中的AGENT_TYPE_LIST
       由props type决定，默认为AGENT_TYPE_LIST[0].value
    3. 该组件还可以调用父组件传入的callback函数，请将函数传入props callback
-->

<script setup>
import { AudioOutlined, ConsoleSqlOutlined, SoundOutlined, PauseCircleFilled, BorderOutlined } from '@ant-design/icons-vue'

import { ref, reactive, watch, onMounted, computed, defineEmits } from 'vue'
import { message } from 'ant-design-vue'
import MarkdownIt from 'markdown-it'
import { DURATION, AGENT_TYPE_LIST } from '../publicConfig'
import { getLocalImg } from '../utils'
import image from '../assets/agent.png'
import {
  sendMessageToAgent,
  textGenerateAudio,
  audioGenerateText,
} from '../request/request'
//  定义props
const props = defineProps({
  size: {
    type: String,
    default: 'middle',
  },
  type: {
    type: String,
    default: AGENT_TYPE_LIST[0].type,
  },
  callback: {
    type: Function,
  },
  agentImg: {
    type: String,
    default: image,
  },
  record: {
    type: Object,
  },
  initialData: {
    type: Object,
    default: {
      // 需要发出去的文本
      message: '',
      // 占位的文本
      placeholder: ''
    }
  },
})
//  样式类名
const styleClass = computed(() => `size_${props.size}`)
const [messageApi, contextHolder] = message.useMessage()
//  输入方式列表
const inputTypeList = ref([
  {
    title: '文字输入',
    iconName: 'keyboard.svg',
    placeholder: '请输入文字',
  },
  {
    title: '语音输入',
    iconName: 'microphone.svg',
    placeholder: '长按说话',
  },
])
//  当前输入方式
const currentInputType = ref(0)
//  切换输入方式
const switchInputType = () => {
  let val = (currentInputType.value + 1) % inputTypeList.value.length
  currentInputType.value = val
  // console.log(currentInputType.value)
}
//  音量列表
const volumeTypeList = ref([
  {
    title: '静音',
    iconName: 'volume.svg',
  },
  {
    title: '不静音',
    iconName: 'volume-up.svg',
  },
])
//  当前输入方式
const currentVolumeType = ref(1)
//  信息列表
//  item的source字段标志来源为ai还是用户
let messageList = reactive([
  // {
  //     answer: '你说什么啊，我不知道啊！',
  //     time: '465465465',
  //     source: 'user'
  // },
  // {
  //     answer: '对不起，我是非常不智能的人工智能小Q！对不起，我是非常不智能的人工智能小Q！对不起，我是非常不智能的人工智能小Q！对不起，我是非常不智能的人工智能小Q！对不起，我是非常不智能的人工智能小Q！',
  //     time: '465465465',
  //     source: 'ai'
  // },
  // {
  //     answer: '你说什么啊，我不知道啊！',
  //     time: '465465465',
  //     source: 'user'
  // },
  // {
  //     answer: '对不起，我是非常不智能的人工智能小Q！对不起，我是非常不智能的人工智能小Q！对不起，我是非常不智能的人工智能小Q！对不起，我是非常不智能的人工智能小Q！对不起，我是非常不智能的人工智能小Q！',
  //     time: '465465465',
  //     source: 'ai'
  // },
  // {
  //     answer: '你说什么啊，我不知道啊！',
  //     time: '465465465',
  //     source: 'user'
  // },
  // {
  //     answer: '对不起，我是非常不智能的人工智能小Q！对不起，我是非常不智能的人工智能小Q！对不起，我是非常不智能的人工智能小Q！对不起，我是非常不智能的人工智能小Q！对不起，我是非常不智能的人工智能小Q！',
  //     time: '465465465',
  //     source: 'ai'
  // },
])
//  信息容器ref
const messageListContainer = ref(null)
const scrollFlag = ref(false)
const messageLoadingStatus = ref(false)
//  切换输入方式
const switchVolumeType = () => {
  let val = (currentVolumeType.value + 1) % volumeTypeList.value.length
  currentVolumeType.value = val
}
//  输入框文字
const inputVal = ref('')

//  Markdown渲染
const renderMarkDown = (text) => {
  //  MarkdownIt实例
  const md = new MarkdownIt({
    html: true,
    linkify: true,
    typographer: true,
  })

  // console.log('渲染的文字', text, md.render(text))
  return md.render(text)
}
//  文字转语音base64字符串
const TTS_str_BASE64 = ref('')
const emit = defineEmits()
const currentEvent = ref('')
//  收到消息后的回调函数
const messageCallback = (msgObj, msg) => {
  currentEvent.value = msgObj.event
  if (!msgObj) {
    if (messageLoadingStatus.value) {
      messageLoadingStatus.value = false
    }
    return msg && message.info(msg)
  }
  let data = null
  if (msgObj.event === 'agent_thought' && msgObj.tool) {
    emit('handleThought', msgObj)
    // broadcastMessage(msgObj.thought)
    if (
      msgObj.tool !== undefined &&
      msgObj.tool === 'openaiuserIdv1projectcreate_post'
    ) {
      data = JSON.parse(msgObj.tool_input)
      console.log('转换后的项目对象', data)
    }
  }
  if (msgObj.event === 'message_end') {
    // let markDownText = md.render(msgObj.thought  || '')
    let mes = messageList.filter((it) => it.message_id === msgObj.message_id)
    let mes_id = mes[0]?.message_id
    for (const key in messageList) {
      if (messageList[key].message_id === mes_id) {
        let mkStr = renderMarkDown(messageList[key].answer)
        // console.log('mkStrmkStr', mkStr)
        messageList[key].markDownText = mkStr
        messageList[key].ready = true
        break
      }
    }
  }
  if (msgObj.event === 'message' || msgObj.event === 'agent_message') {
    if (messageLoadingStatus.value) {
      messageLoadingStatus.value = false
    }
    let mes = messageList.filter((it) => it.message_id === msgObj.message_id)
    if (mes.length === 0) {
      messageList.push({
        ...msgObj,
        time: new Date().getTime(),
        source: 'ai',
      })
    } else {
      let mes_id = mes[0].message_id
      for (const key in messageList) {
        if (messageList[key].message_id === mes_id) {
          messageList[key].answer += msgObj.answer
        }
      }
    }
  }
  props.callback && props.callback(msgObj, data)
  //  ！！！语音播报部分还有问题！！！
  // else if (msgObj.event === 'tts_message') {
  //     TTS_str_BASE64.value += msgObj.audio
  // } else if (msgObj.event === 'tts_message_end') {

  //     TTS_str_BASE64.value = 'data:audio/x-wav;base64' + TTS_str_BASE64.value
  //     console.log('tts_message_end', TTS_str_BASE64.value)
  //     let audioObj = new Audio(`${TTS_str_BASE64.value}`)
  //     audioObj.volume = .7
  //     audioObj.play()
  // }
}

let ctrls = []
//  按下回车处理事件
const handlePressEnter = async () => {
  if (!inputVal.value) return
  messageLoadingStatus.value = true
  messageList.push({
    answer: inputVal.value ,
    time: new Date().getTime(),
    source: 'user',
  })
  let obj = {
    query: inputVal.value,
    type: props.type,
    conversationId: '',
  }
  let conversation_idSourceArr = messageList.filter((it) => it.source === 'ai')

  if (conversation_idSourceArr.length > 0) {
    obj.conversationId = conversation_idSourceArr[0].conversation_id
  }
  if (!obj.conversationId) {
    delete obj.conversationId
  }
  ctrls = new Array(messageList.length+1).fill().map(() => (new AbortController()))
  // console.log('obj', obj, conversation_idSourceArr)
  sendMessageToAgent(obj, messageCallback, ctrls[messageList.length-1].signal)
  setTimeout(() => {
    inputVal.value = ''
    scrollFlag.value = false
    messageListScrollToBottom()
  })
}
//  录音与转文字
const recordStatus = ref(false)
let mediaRecorder = null
let chunks = []
//  开始录音
const startRecognition = () => {
  window.navigator.mediaDevices
    .getUserMedia({
      audio: true,
    })
    .then((stream) => {
      //  创建MediaRecorder实例
      mediaRecorder = new window.MediaRecorder(stream)
      //  绑定数据流push事件
      mediaRecorder.ondataavailable = (e) => {
        chunks.push(e.data)
      }
      //  开始录音
      mediaRecorder.start()
      recordStatus.value = true
    })
    .catch((e) => {
      console.error('录音时发生错误！', e)
    })
}
//  停止录音
const stopRecognition = () => {
  //  绑定录音截止时事件
  mediaRecorder.onstop = async () => {
    const blob = new Blob(chunks, {
      type: 'audio/wav',
    })
    chunks = []
    const file = new File([blob], 'record.wav', {
      type: 'audio/wav',
    })
    let res = await audioGenerateText({
      type: props.type,
      file,
      user: 'utaha456',
    })
    console.log('语音转文字结果', res)
    if (res?.data?.code === 200) {
      inputVal.value = res.data.data.text
    } else {
      messageApi.error('语音转文字失败！', DURATION)
    }
  }
  //  停止录音
  mediaRecorder.stop()
  recordStatus.value = false
}

//  信息列表更新时自动滚动到底端
const messageListScrollToBottom = () => {
  console.log(scrollFlag.value, 'scrollFlag.value')
  if (scrollFlag.value) return
  let temp = (messageListContainer.value.scrollTop =
    messageListContainer.value.scrollHeight -
    messageListContainer.value.clientHeight)
  temp = temp < 0 ? 0 : temp
  messageListContainer.value.scrollTop = temp
}
//  语音播报ai的话
const broadcastMessage = async (str) => {
  if (currentVolumeType.value === 0) {
    return
  }
  let text = ''
  if (!str && messageList[messageList.length - 1].source !== 'user') {
    text = messageList[messageList.length - 1].answer
  }
  if (str) {
    text = str
  }
  // let res = await textGenerateAudio({
  //     type: props.type,
  //     text,
  //     user: 'utaha456',
  // })
  // console.log('语音转文字', res)
  // if (res?.data?.code === 200) {
  //     console.log(res)
  //     let audioObj = new Audio(res.data.data.endpoints[0].url)
  //     audioObj.volume = .7
  //     audioObj.play()
  // } else {
  //     messageApi.error('语音生成错误！', DURATION)
  // }
}

const initialOperation = () => {
  console.log('initialOperation called with:', props.initialData)
  if (props.initialData.message) {
    inputVal.value = props.initialData.message
    handlePressEnter()
  }
	if (props.initialData.placeholder){		
		inputTypeList.value[currentInputType.value].placeholder = props.initialData.placeholder
	}
	
  setTimeout(() => {
   
  }, 50)
}
watch(messageList, messageListScrollToBottom, {
  flush: 'post',
})
console.log(props.record)
const handleStop = () => {
  console.log(messageList, messageList.length, ctrls)
  ctrls[messageList.length-1].abort()
}
watch(() => props.record, (newval) => {
  ctrls.map(val => val.abort())
  messageList = reactive([])
  inputVal.value = ''
  messageLoadingStatus.value = false
  initialOperation()
})


onMounted(async () => {
  initialOperation()
  scrollFlag.value = false
  let lastScrollTop = 0
  messageListContainer.value.addEventListener('scroll', e => {
    console.log('scroll', e);
    if (e.isTrusted) {
      const currentScrollTop = messageListContainer.value.scrollTop;
      if (currentScrollTop < lastScrollTop) scrollFlag.value = true
      else scrollFlag.value = false
      lastScrollTop = currentScrollTop;
    }
  })
  messageListScrollToBottom()
})
</script>

<style lang="scss" scoped>
$containerMargin: 8px;
$sendMessageHeight: 65px;

.AIConsulting_container {
  overflow: auto;
  position: relative;
  min-width: 0;
  min-height: 0;
  max-width: 100%;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 10px;
  overflow: hidden;
  border-radius: 12px;
  &.size_small, &.size_clear {
    position: relative;
  }
  .AIConsulting_message_container,
  .AIConsulting_agent_container {
    z-index: 2;
    &.size_small, &.size_clear {
      width: 100% !important;
    }
  }
   
  .AIConsulting_agent_container {
    margin-top: 20px;
    margin-bottom: 10px;
    background: center bottom/auto 100% no-repeat;
    position: relative;
    .AIConsulting_agent_sendMessageToAgentContainer {
      $btnBgColor: rgba($theme1BgColorWhite, 0.7);
      $btnBgHoverColor: rgba($theme1BgColorWhite, 1);
      $btnBgActiveColor: rgba($theme1BgColorWhite, 0.2);
      $btnBgDisabledColor: rgba(#aaa, 0.2);
      flex-grow: 1;
      padding: 10px 20px;
      background-color: $btnBgColor;
      border-radius: calc($sendMessageHeight/2);
      box-shadow: $generalBoxShadow;
      overflow: hidden;
      @include FlexCenterAndCenter;
      .AIConsulting_agent_switchTypeContainer {
        width: 15%;
        height: 100%;
        /* padding-left: calc($sendMessageHeight/2); */
        border-radius: calc($sendMessageHeight/2) 0 0 calc($sendMessageHeight/2);
        // @include btnBasicStyle;
        // background-color: $btnBgColor;
        & > img {
          $imgWidth: 60%;
          width: $imgWidth;
          height: $imgWidth;
        }
      }
      .AIConsulting_agent_switchTypeContainer:hover {
        // background-color: $btnBgHoverColor;
      }
      .AIConsulting_agent_switchTypeContainer:active {
        // background-color: $btnBgActiveColor;
      }
      .AIConsulting_agent_sendMessageToAgent {
        width: 85%;
        height: 100%;
        border-radius: 0 calc($sendMessageHeight/2) calc($sendMessageHeight/2) 0;
        display: flex;
        align-items: center;
      }
      .AIConsulting_agent_sendMessageToAgent {
        .AIConsulting_agent_sendMessageToAgent_input {
          width: 100%;
          height: 30px;
          font-size: $theme1FontSize + 2px;
        }
      }
      .AIConsulting_agent_sendMessageToAgent_microphone {
        width: 100%;
        height: 100%;
        @include btnBasicStyle;
        background-color: $btnBgColor;
        border-radius: 0 calc($sendMessageHeight/2) calc($sendMessageHeight/2) 0;
        font-size: $theme1FontSize + 4px;
        letter-spacing: 0.04em;
      }
      .AIConsulting_agent_sendMessageToAgent_microphone {
        &:hover {
          background-color: $btnBgHoverColor;
        }
        &:active {
          background-color: $btnBgActiveColor;
        }
      }
      .AIConsulting_agent_sendMessageToAgent_mute {
        width: 26px;
        height: 26px;
        @include btnBasicStyle;

        & > img {
          $imgWidth: 100%;
          width: $imgWidth;
          height: $imgWidth;
        }
      }
    }
  }
  .size_clear {
    margin-bottom: 0px;
  }
  .AIConsulting_message_container {
    flex: .8;
    overflow: hidden auto;
    display: flex;
    flex-direction: column;
    position: relative;
    @include hoverShowScrollBar;
    &.size_small {
      width: calc(90% - $containerMargin - $containerMargin);
      border-radius: $generalBorderRadiusBig;
      background-color: rgba($theme1BgColorLight, 0.5);
    }
    &.size_clear {
      flex: 1;
      width: calc(90% - $containerMargin - $containerMargin);
      border-radius: $generalBorderRadiusBig;
      background-color: rgba($theme1BgColorLight, 0.5);
      margin-bottom: 0px !important;
    }
    .AIConsulting_message_loading {
      @include FlexCenterAndCenter;
      width: 100%;
      height: 40px;
      transition: $generalTransition;
      transition-duration: 0.5s;
      @keyframes loading_dot_ani_1 {
        0% {
          transform: translateY(0%);
        }
        15% {
          transform: translateY(-50%);
        }
        30% {
          transform: translateY(0%);
        }
        45% {
        }
        60% {
        }
        75% {
        }
        90% {
        }
      }
      @keyframes loading_dot_ani_2 {
        0% {
        }
        15% {
        }
        30% {
          transform: translateY(0%);
        }
        45% {
          transform: translateY(-50%);
        }
        60% {
          transform: translateY(0%);
        }
        75% {
        }
        90% {
        }
      }
      @keyframes loading_dot_ani_3 {
        0% {
        }
        15% {
        }
        30% {
        }
        45% {
        }
        60% {
          transform: translateY(0%);
        }
        75% {
          transform: translateY(-50%);
        }
        90% {
          transform: translateY(0%);
        }
      }
      @for $i from 1 through 3 {
        & > .AIConsulting_message_loading_dot_#{$i} {
          animation: loading_dot_ani_#{$i} 1.25s linear 0s infinite normal none;
        }
      }
      & > .AIConsulting_message_loading_dot {
        $dotWidth: 10px;
        width: $dotWidth;
        height: $dotWidth;
        border-radius: 50%;
        background-color: gray;
        margin: 0 10px;
      }
    }
    .AIConsulting_message_loading_hidden {
      transform: scaleY(0);
    }
    .AIConsulting_message_item,
    .AIConsulting_message_item_ai {
      position: relative;
      z-index: 3;
      max-width: 70%;
      height: auto;
      border-radius: $generalBorderRadius;
      background-color: rgba($theme1BgColorWhite, 1);
      box-shadow: $generalBoxShadow;
      padding: 15px;
      margin: 10px 15px;
      line-height: 1.6em;
      font-size: 14px;
    }
    .AIConsulting_message_item {
      background-color: rgba($theme1BgColorDeep, 0.5);
      color: $theme1FontColorWhite;
      align-self: flex-end;
    }
  }
  .stop {
    color: #333;
    border: 1px solid #333;
    min-width: 30px;
    min-height: 30px;
    border-radius: 50%;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 12px;
  }
}
.agentImg {
  background: center bottom/auto 100% no-repeat;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

</style>
<style>
p {
        margin-bottom: 0 !important;
      }</style>